`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;

localparam          LEN_BW                  = 4;    // data byte number(exclude addr byte) for each read or write

reg                                         rst_n;
reg                                         clk;

reg                                         trans_req;
reg                 [7-1:0]                 trans_adr;  // slave device address
reg                                         trans_rw;   // 1'b0:transmit data to slave; 1'b1:receive data from slave
reg                 [LEN_BW-1:0]            trans_len;  // data byte number(exclude addr byte) for each read or write
wire                                        trans_ack;
wire                                        trans_fail; // NAK detected

wire                                        tx_rdy;     // take one byte for TX
wire                [LEN_BW-1:0]            tx_seq;
reg                 [8-1:0]                 tx_data;

wire                                        rx_vld;     // get one byte from RX
wire                [LEN_BW-1:0]            rx_seq;
wire                [8-1:0]                 rx_data;

reg                 [8-1:0]                 dat_buf[15:0];
reg                 [8-1:0]                 i2c_buf[15:0];

// I2C signals
tri1                                        scl;
tri1                                        sda;

wire                                        scl_od;
wire                                        sda_od;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_i2c", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_RW
    trans_req = 0;
    trans_adr = 0;
    trans_rw = 0;
    trans_len = 0;

    @(posedge rst_n);
    @(posedge clk);

    i2c_wr_t(10, 7'h47, 12, 8'hd1);
    i2c_rd_t(10, 7'h47, 12);

    #1_000;
    rgrs.one_chk_done("gen is done.");
end

i2c_mst #(
        .CLK_DIV                        (2                              ),	// CLK_DIV>=2, frequency of scl = Fclk/2/CLK_DIV
        .LEN_BW                         (LEN_BW                         )	// data byte number(exclude addr byte) for each read or write
) u_i2c_mst ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .trans_req                      (trans_req                      ),
        .trans_adr                      (trans_adr                      ),	// slave device address
        .trans_rw                       (trans_rw                       ),	// 1'b0:transmit data to slave; 1'b1:receive data from slave
        .trans_len                      (trans_len                      ),	// data byte number(exclude addr byte) for each read or write
        .trans_ack                      (trans_ack                      ),
        .trans_fail                     (trans_fail                     ),	// NAK detected

        .tx_rdy                         (tx_rdy                         ),	// take one byte for TX
        .tx_seq                         (tx_seq                         ),
        .tx_data                        (tx_data                        ),

        .rx_vld                         (rx_vld                         ),	// get one byte from RX
        .rx_seq                         (rx_seq                         ),
        .rx_data                        (rx_data                        ),

// I2C signals
        .scl_in                         (scl                            ),
        .scl_od                         (scl_od                         ),	// 1'b0:output to GND; 1'b1:output to high-z
        .sda_in                         (sda                            ),
        .sda_od                         (sda_od                         )	// 1'b0:output to GND; 1'b1:output to high-z
);

assign scl = (scl_od==1'b0) ? 1'b0 : 1'bz;
assign sda = (sda_od==1'b0) ? 1'b0 : 1'bz;

reg                                         scl_dly;
reg                                         sda_dly;
wire                                        i2c_start;
wire                                        i2c_stop;

reg                                         i2c_pre_ind;
reg                                         i2c_adr_ind;
reg                                         i2c_rdat_ind;
reg                 [4-1:0]                 i2c_bit;
reg                 [8-1:0]                 i2c_shift;
reg                                         i2c_rw;
reg                 [LEN_BW-1:0]            i2c_byte;

wire                                        i2c_ack;
wire                                        i2c_rdat;

initial begin
    scl_dly = 1'b1;
    sda_dly = 1'b1;

    i2c_pre_ind = 1'b0;
    i2c_adr_ind = 1'b0;
    i2c_rdat_ind = 1'b0;
    i2c_bit = 0;
    i2c_shift = 0;
    i2c_rw = 0;
    i2c_byte = 0;
    i2c_buf[0] = 0;
end

always@(scl) begin
    scl_dly <=#5 scl;
end

always@(sda) begin
    sda_dly <=#5 sda;
end

assign i2c_start = scl_dly  & scl & sda_dly & (~sda);
assign i2c_stop  = scl_dly  & scl & (~sda_dly) & sda;

always@(posedge i2c_start or negedge scl) begin
    if (i2c_start==1'b1) begin
        i2c_pre_ind <=`U_DLY 1'b1;
        i2c_adr_ind <=`U_DLY 1'b1;
        i2c_rdat_ind <=`U_DLY 1'b0;
        i2c_bit <=`U_DLY 0;
        i2c_shift <=`U_DLY 0;
        i2c_rw <=`U_DLY 0;
        i2c_byte <=`U_DLY 0;
    end else begin
        i2c_pre_ind <=`U_DLY 1'b0;
        if (i2c_pre_ind==1'b0) begin
            i2c_bit <=`U_DLY (i2c_bit>=8) ? 0 : i2c_bit + 1;
            if (i2c_bit<=4'd7) begin
                i2c_shift <=`U_DLY {i2c_shift[6:0], sda};
            end
        end

        if (i2c_adr_ind==1'b1 && i2c_bit==4'd7) begin
            i2c_rw <=`U_DLY sda;
        end

        if (i2c_bit>=4'd8) begin
            i2c_adr_ind <=`U_DLY 1'b0;
            if (i2c_adr_ind==1'b0) begin
                i2c_byte <=`U_DLY i2c_byte + 1;
            end
            if (i2c_adr_ind==1'b0 && sda==1'b1) begin
                i2c_rdat_ind <=`U_DLY 1'b0;
            end else if (i2c_adr_ind==1'b1 && i2c_rw==1'b1) begin
                i2c_rdat_ind <=`U_DLY 1'b1;
            end

            if (i2c_rw==1'b1) begin
                i2c_shift <=`U_DLY (i2c_adr_ind==1'b1) ? i2c_buf[0] : i2c_buf[i2c_byte+1];;
            end else if (i2c_adr_ind==1'b0) begin
                i2c_buf[i2c_byte] <=`U_DLY i2c_shift;
            end
        end
    end
end

always@(posedge i2c_stop) begin
    if (i2c_rw==1'b0) begin
        i2c_buf[0] = i2c_buf[0] + 8'h20;
    end else begin
        i2c_rdat_ind <=`U_DLY 1'b0;
    end
end

assign i2c_ack = (i2c_bit==4'd8 && (i2c_adr_ind==1'b1 || i2c_rw==1'b0)) ? 1'b0 : 1'b1;
assign i2c_rdat = (i2c_rdat_ind==1'b1 && i2c_bit<4'd8) ? i2c_shift[7] : 1'b1;

assign sda = (i2c_ack==1'b0 || i2c_rdat==1'b0) ? 1'b0 : 1'bz;


task i2c_wr_t;
    input integer           gap;
    input [6:0]             adr;
    input [LEN_BW-1:0]      len;
    input [7:0]             dat;
    
    repeat(gap) begin
        @(posedge clk);
    end
    trans_req <=`U_DLY 1'b1;
    trans_adr <=`U_DLY adr;
    trans_rw  <=`U_DLY 1'b0;
    trans_len <=`U_DLY len;

    tx_data <=`U_DLY dat;

    @(posedge clk);
    while(trans_ack==1'b0) begin
        if (tx_rdy==1'b1) begin
            tx_data <=`U_DLY tx_data + 1'd1;
        end
        @(posedge clk);
    end

    trans_req <=`U_DLY 1'b0;
endtask

task i2c_rd_t;
    input integer           gap;
    input [6:0]             adr;
    input [LEN_BW-1:0]      len;
    
    repeat(gap) begin
        @(posedge clk);
    end
    trans_req <=`U_DLY 1'b1;
    trans_adr <=`U_DLY adr;
    trans_rw  <=`U_DLY 1'b1;
    trans_len <=`U_DLY len;

    @(posedge clk);
    while(trans_ack==1'b0) begin
        if (rx_vld==1'b1) begin
            dat_buf[rx_seq] <=`U_DLY rx_data;
        end
        @(posedge clk);
    end

    trans_req <=`U_DLY 1'b0;
endtask

endmodule

